﻿using System;
using System.Collections.Generic;
using System.Linq;
using BackTask.Model;
using BackTask.SqlServer.SqlServerAccessor;
using Newtonsoft.Json.Linq;

namespace BackTask.SqlServer
{
    public class SqlServerConfiguration : BackTaskConfiguration
    {
        public override StorageTypes StorageType => StorageTypes.SqlServer;

        protected override List<TaskJob> Jobs { get; set; } = new List<TaskJob>();

        private string connectionString;
        private IEnumerable<string> configurations;
        private readonly SqlServerConnection sqlServerConnection;

        public SqlServerConfiguration()
        {
            SqlServerConnection sqlServerConnection = new SqlServerConnection();
            this.sqlServerConnection = sqlServerConnection;
        }

        public override IEnumerable<TaskJob> BindJobs()
        {
            foreach (var configuration in configurations)
            {
                JObject @object = JObject.Parse(configuration);
                if (@object.TryGetValue("type", out JToken? value))
                {
                    string val = value.ToString();
                    TaskJob job;
                    if (val?.ToLower() == TaskBuilderTypes.Cron.ToString().ToLower())
                    {
                        job = BindCronTaskJob(@object);
                    }
                    else if (val?.ToLower() == TaskBuilderTypes.Simple.ToString().ToLower())
                    {
                        job = BindSimpleTaskJob(@object);
                    }
                    else
                    {
                        throw new Exception("unsupport task type");
                    }

                    Jobs.Add(job);
                }
            }

            return Jobs;
        }

        private TaskJob BindCronTaskJob(JObject @object)
        {
            string url = @object.GetValue("url").ToString();
            if (url.IsNullOrEmptyOrWithSpace()) throw new Exception("request url is null or empty");
            string cron = @object.GetValue("cron").ToString();
            if (url.IsNullOrEmptyOrWithSpace()) throw new Exception("cron expression is empty");
            string name = @object.GetValue("name").ToString();
            string priorityValue = @object.GetValue("priority").ToString();
            int priority = priorityValue.IsNullOrEmptyOrWithSpace() ? 0 : Convert.ToInt32(priorityValue);

            string description = @object.GetValue("description").ToString();
            string groupName = @object.GetValue("groupName").ToString();
            groupName = groupName.IsNullOrEmptyOrWithSpace() ? Constants.DEFAULT_GROUP_NAME : groupName;
            if (HasSameJob(name, groupName))
            {
                throw new Exception($"name: {name},groupName: {groupName}, job has been added");
            }


            return new CronTaskJob(cron, url, name, priority, description, groupName);
        }

        private TaskJob BindSimpleTaskJob(JObject @object)
        {
            string name = @object.GetValue("name").ToString() ?? DateTime.Now.ToString();
            string url = @object.GetValue("url").ToString();
            if (url.IsNullOrEmptyOrWithSpace()) throw new Exception("request url is null or empty");

            JEnumerable<JToken> configJTokens = @object.GetValue("config").Children();
            string executeTypeValue = configJTokens.FirstOrDefault(r => ((JProperty)r).Name == "executeType")?.First.ToString();
            string executeCountValue = configJTokens.FirstOrDefault(r => ((JProperty)r).Name == "executeCount")?.First.ToString();
            int executeCount = executeCountValue.IsNullOrEmptyOrWithSpace() ? 1 : Convert.ToInt32(executeCountValue);

            string repeatForeverValue = configJTokens.FirstOrDefault(r => ((JProperty)r).Name == "repeatForever")?.First.ToString();
            bool repeatForever = repeatForeverValue.IsNullOrEmptyOrWithSpace() ? false : repeatForeverValue.ToLower() == "true";

            string intervalValue = configJTokens.FirstOrDefault(r => ((JProperty)r).Name == "interval")?.First.ToString();
            int interval = intervalValue.IsNullOrEmptyOrWithSpace() ? 1 : Convert.ToInt32(intervalValue);
            string intervalTypeValue = configJTokens.FirstOrDefault(r => ((JProperty)r).Name == "intervalType")?.First.ToString();
            SimpleTaskJobIntervalTypes intervalType = intervalTypeValue.IsNullOrEmptyOrWithSpace() ? SimpleTaskJobIntervalTypes.Second : intervalTypeValue.GetEnumByDescription<SimpleTaskJobIntervalTypes>();

            string priorityValue = configJTokens.FirstOrDefault(r => ((JProperty)r).Name == "priority")?.First.ToString();
            int priority = priorityValue.IsNullOrEmptyOrWithSpace() ? 0 : Convert.ToInt32(priorityValue);

            string description = @object.GetValue("description").ToString();
            string groupName = @object.GetValue("groupName").ToString();
            groupName = groupName.IsNullOrEmptyOrWithSpace() ? Constants.DEFAULT_GROUP_NAME : groupName;

            if (HasSameJob(name, groupName))
            {
                throw new Exception($"name: {name},groupName: {groupName}, job has been added");
            }

            SimpleTaskExecuteConfig executeConfig = new SimpleTaskExecuteConfig()
            {
                Interval = interval,
                RepeatForever = repeatForever,
                IntervalType = intervalType,
                ExecuteCount = executeCount,
            };

            return new SimpleTaskJob(url, name, executeConfig, priority, description, groupName);
        }

        public override void BuildConfiguration()
        {
            IEnumerable<string> configurations = this.sqlServerConnection.QueryConfiguration();
            this.configurations = configurations;
        }

        public override void UseConfiguration(object configuration)
        {
            this.connectionString = configuration.ToString();
            this.sqlServerConnection.EnableCreated(this.connectionString);
        }

        public override void DeleteConfiguration(string name, string groupName)
        {
            var willDeleteJob = Jobs.FirstOrDefault(r => r.Name == name && r.GroupName == groupName);
            Jobs.Remove(willDeleteJob);
            this.sqlServerConnection.DeleteConfiguration(name, groupName);
        }

        public override void AddConfiguration(TaskJob job)
        {
            Jobs.Add(job);
            this.sqlServerConnection.AddConfiguration(job);
        }

        public override void UpdateConfiguration(TaskJob job)
        {
            Jobs.Remove(Jobs.FirstOrDefault(r => r.Name == job.Name && r.GroupName == job.GroupName));
            Jobs.Add(job);
            this.sqlServerConnection.UpdateConfiguration(job);
        }
    }
}
